#include "Entity/Entity.h"
#include "Entity.h"

Entity::Entity() : IMotion(), IRotation(), IGameObject(), IVisible(), ICollision()
{
    for (int i = 0; i < sizeof(slots) / sizeof(slots[0]); i++)
    {
        slots[i].part =nullptr;
    }
}

Entity::~Entity()
{
    if (getCollisionEntity() != nullptr)
    {
        getCollisionEntity()->setParent(nullptr);
        delete getCollisionEntity();
        setCollisionEntity(nullptr);
    }
    for (int i = 0; i < sizeof(slots) / sizeof(slots[0]); i++)
    {
        if (slots[i].part != nullptr)
        {
            delete slots[i].part;
            slots[i].part = nullptr;
        }
    }
}

real Entity::rotate(real delta, real target_angle)
{
    real before = getFacing();
    real rest = IRotation::rotateTo(delta, target_angle);
    real dangle = so_angle(getFacing() - before);
    if (!dangle)
    {
        return rest;
    }
    // for (int i = 0; i < 4; ++i)
    // {
    //     if (slots[i].part == nullptr)
    //     {
    //         continue;
    //     }
    //     else if (slots[i].part->actions & (int)PartAction::MOVE)
    //     {
    //         slots[i].part->setFacing(getFacing());
    //     }
    //     else if (slots[i].part->actions & (int)PartAction::ATTACK)
    //     {
    //         slots[i].part->setFacing(so_angle(slots[i].part->getFacing() + dangle));
    //     }
    // }
    return rest;
}

bool Entity::move(real delta, Vector2 target)
{

    real rad = so_atan2(target.y - getPosition().y, target.x - getPosition().x);
    real angle = rad * 180 / PI;
    real d2 = (target - getPosition()).length2();
    real d = getVelocity() * delta;
    if (rotate(delta, angle) > allowMoveFacingDifference)
    {
        return false;
    }
    if (d * d > d2)
    {
        IMotion::setPosition(target);
        return true;
    }
    IMotion::motion(delta, rad);
    getCollisionEntity()->updatePosition(getPosition());
    return false;
}

bool Entity::approach(real delta, Vector2 target, real min_distance)
{
    real rad = so_atan2(target.y - getPosition().y, target.x - getPosition().x);
    real angle = rad * 180 / PI;
    real d2 = (target - getPosition()).length2();
    real d = getVelocity() * delta;
    if (d == 0)
    {
        return true;
    }
    if (rotate(delta, angle) > allowMoveFacingDifference)
    {
        return false;
    }
    if (d * d > d2)
    {
        IMotion::setPosition(target);
        return true;
    }
    if (d2 <= min_distance * min_distance)
    {
        return true;
    }
    IMotion::motion(delta, rad);
    getCollisionEntity()->updatePosition(getPosition());
    return false;
}

void Entity::update(real delta)
{
    IMotion::update(delta);
    for (int i = 0; i < 4; ++i)
    {
        if (slots[i].part == nullptr)
        {
            continue;
        }
        real length = slots[i].position.length();
        real rad = getFacing() * PI / 180 - so_atan2(slots[i].position.y, slots[i].position.x) + PI / 2;
        slotsPosition[i] = getPosition() + Vector2(length * so_cos(rad), length * so_sin(rad));
        if (slots[i].part->actions & (int)PartAction::ATTACK || slots[i].part->actions & (int)PartAction::HARVEST)
        {
            if (slots[i].part->follow != nullptr)
            {
                Entity *target = (Entity *)slots[i].part->follow;
                CollisionEntityCircle *cir = dynamic_cast<CollisionEntityCircle *>(target->getCollisionEntity());
                real range = slots[i].part->followRange;
                if (cir)
                {
                    range += cir->getRadius();
                }
                if ((getPosition() - target->getPosition()).length2() <= range * range)
                {
                    Vector2 pos = getSlotPosition(i);
                    Vector2 dv = target->getPosition() - pos;
                    real angle = so_atan2(dv.y, dv.x) * 180 / PI;
                    slots[i].part->rotateTo(delta, angle);
                }
                else
                {
                    slots[i].part->rotateTo(delta, getFacing());
                }
            }
            else
            {
                slots[i].part->rotateTo(delta, getFacing());
            }
        }
        else if (slots[i].part->actions & (int)PartAction::MOVE)
        {
            if (((int)orderType & (int)UnitOrderType::MOVE))
            {
                slots[i].part->animation->setVisible(true);
            }
            else
            {
                slots[i].part->animation->setVisible(false);
            }
            slots[i].part->setFacing(getFacing());
        }
        if (slots[i].part->follow != nullptr && slots[i].part->follow->isExited())
        {
            slots[i].part->follow = nullptr;
        }
    }
}

void Entity::setSlot(Part *part, int slot)
{
    if (slot > sizeof(slots) / sizeof(slots[0]) - 1)
    {
        return;
    }
    if (slots[slot].part != nullptr)
    {
        delete slots[slot].part;
        slots[slot].part = nullptr;
    }
    slots[slot].part = part;
}

Slot *Entity::getSlots()
{
    return slots;
}

Vector2 Entity::getSlotPosition(int index)
{
    if (index < 0 || index > 3)
    {
        return Vector2();
    }
    return slotsPosition[index];
}
